package org.koin.core.context;

import org.koin.core.Koin;
import org.koin.core.KoinApplication;
import org.koin.core.error.DefinitionOverrideException;
import org.koin.core.error.KoinAppAlreadyStartedException;
import org.koin.core.error.NoScopeDefFoundException;
import org.koin.core.error.ScopeAlreadyCreatedException;
import org.koin.core.module.Module;
import java.util.Arrays;
import java.util.List;

public class GlobalContext implements KoinContext {

    private static volatile GlobalContext INSTANCE;

    private Koin _koin;
    private KoinApplication _koinApplication;

    private GlobalContext() {

    }

    public static GlobalContext INSTANCE() {
        if (INSTANCE == null) {
            synchronized (GlobalContext.class) {
                if (INSTANCE == null) {
                    INSTANCE = new GlobalContext();
                }
            }
        }
        return INSTANCE;
    }

    @Override
    public Koin get() {
        synchronized (this) {
            if (_koin != null) {
                return _koin;
            } else {
                throw new IllegalStateException("KoinApplication has not been started");
            }
        }
    }

    @Override
    public Koin getOrNull() {
        return _koin;
    }

    public KoinApplication getKoinApplicationOrNull() {
        return _koinApplication;
    }

    private void register(KoinApplication koinApplication) throws KoinAppAlreadyStartedException {

        if (_koin != null) {
            throw new KoinAppAlreadyStartedException("A Koin Application has already been started");
        }
        _koinApplication = koinApplication;
        _koin = koinApplication.getKoin();
    }

    @Override
    public void stopKoin() {
        synchronized (this) {
            if (_koin != null) {
                _koin.close();
            }
            _koin = null;
        }
    }

    @Override
    public KoinApplication startKoin(KoinApplication koinApplication)
            throws KoinAppAlreadyStartedException {
        synchronized (this) {
            register(koinApplication);
            koinApplication.createEagerInstances();
            return koinApplication;
        }
    }

    @Override
    public KoinApplication startKoin(org.koin.dsl.KoinAppDeclaration appDeclaration)
            throws ScopeAlreadyCreatedException,
            NoScopeDefFoundException,
            KoinAppAlreadyStartedException {
        synchronized (this) {
            KoinApplication koinApplication = KoinApplication.init();
            register(koinApplication);
            appDeclaration.invoke(koinApplication);
            koinApplication.createEagerInstances();
            return koinApplication;
        }
    }

    @Override
    public void loadKoinModules(Module module) {
        synchronized (this) {
            try {
                get().loadModules(Arrays.asList(module));
            } catch (DefinitionOverrideException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void loadKoinModules(List<Module> modules) {
        synchronized (this) {
            try {
                get().loadModules(modules);
            } catch (DefinitionOverrideException e) {
                e.printStackTrace();
            }

        }
    }

    @Override
    public void unloadKoinModules(Module module) {
        synchronized (this) {
            get().unloadModules(Arrays.asList(module));
        }
    }

    @Override
    public void unloadKoinModules(List<Module> modules) {
        synchronized (this) {
            get().unloadModules(modules);
        }
    }
}
